# coding: utf-8
import io
import math
import os
import random

from PIL import Image, ImageFont, ImageDraw
from PIL.ImageFile import ImageFile


class Captcha(object):
    def __init__(self):
        self.__type = 'en'
        self.__font_file = 'fonts/PepRally.ttf'
        self.__font_size = 50
        self.__font_color = (0, 0, 0)
        self.__font_rand_a = 10
        self.__font_rand_b = 2
        self.__font_rotate_min = 0
        self.__font_rotate_max = 0
        self.__font_distance = 40

        self.__board_width = 200
        self.__board_height = 100
        self.__board_bgcolor = (255, 255, 255)

        self.__line_num = 3
        self.__line_width = 1
        self.__line_color = (0, 0, 0)
        self.__line_rand_a = 15
        self.__line_rand_b = 5

        self.__board: ImageFile = None
        self.__text = ''
        self.__has_generated = False

    def set_type(self, type):
        self.__type = type

    def set_font_file(self, font_file):
        self.__font_file = font_file

    def set_font_size(self, font_size):
        self.__font_size = font_size

    def set_font_color(self, *font_color):
        self.__font_color = font_color

    def set_font_rand_a(self, rand_a):
        self.__font_rand_a = rand_a

    def set_font_rand_b(self, rand_b):
        self.__font_rand_b = rand_b

    def set_font_rotate(self, rotate_min, rotate_max):
        self.__font_rotate_min = rotate_min
        self.__font_rotate_max = rotate_max

    def set_font_distance(self, distance):
        self.__font_distance = distance

    def set_board_width(self, width):
        self.__board_width = width

    def set_board_height(self, height):
        self.__board_height = height

    def set_board_bgcolor(self, *bgcolor):
        self.__board_bgcolor = bgcolor

    def set_line_num(self, num):
        self.__line_num = num

    def set_line_width(self, width):
        self.__line_width = width

    def set_line_color(self, *color):
        self.__line_color = color

    def set_line_rand_a(self, rand_a):
        self.__line_rand_a = rand_a

    def set_line_rand_b(self, rand_b):
        self.__line_rand_b = rand_b

    def __with_sin(self, im, rand_a, rand_b):
        pixes = im.load()
        pic_width, pic_height = im.size
        im2 = Image.new('RGBA', (pic_width, pic_height), color=(0, 0, 0, 0))
        pixes2 = im2.load()

        for x in range(pic_width):
            for y in range(pic_height):
                newx = x + math.sin(y / rand_a) * rand_b
                newy = y + math.sin(newx / rand_a) * rand_b
                newx = x + math.sin(newy / rand_a) * rand_b

                newx = int(max(0, min(newx, pic_width - 1)))
                newy = int(max(0, min(newy, pic_height - 1)))

                pixes2[x, y] = pixes[newx, newy]
        return im2

    def __get_im(self):
        pic_width = 80
        pic_height = 80

        im = Image.new('RGBA', (pic_width, pic_height), color=(0, 0, 0, 0))

        font_file_path = os.path.join(os.path.dirname(__file__), self.__font_file)
        font = ImageFont.truetype(font_file_path, self.__font_size)

        char = random.sample('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 1)[0]
        self.__text += char

        draw = ImageDraw.Draw(im)
        draw.text((10, 10), char, font=font, fill=self.__font_color)

        im = self.__with_sin(im, self.__font_rand_a, self.__font_rand_b)

        im = im.rotate(random.randint(self.__font_rotate_min, self.__font_rotate_max) * random.sample([1, -1], 1)[0])

        return im

    def __get_curve(self):
        im = Image.new('RGBA', (self.__board_width, self.__board_height), (0, 0, 0, 0))
        draw = ImageDraw.Draw(im)
        line = [(random.randint(0, self.__board_width - 1), random.randint(0, self.__board_height - 1)),
                 (random.randint(0, self.__board_width - 1), random.randint(0, self.__board_height - 1))]
        draw.line(line, fill=self.__line_color, width=self.__line_width)

        im = self.__with_sin(im, self.__line_rand_a, self.__line_rand_b)

        return im

    def __create_board(self):
        self.__board = Image.new('RGB', (self.__board_width, self.__board_height), self.__board_bgcolor)

    def __add_im(self):
        x = 0
        for i in range(4):
            im = self.__get_im()
            self.__board.paste(im, (x, 0), mask=im)
            x = x + self.__font_distance

    def __add_curve(self):
        for i in range(self.__line_num):
            im_curve = self.__get_curve()
            self.__board.paste(im_curve, (0, 0), mask=im_curve)

    def __generate_image(self):
        if not self.__has_generated:
            self.__create_board()
            self.__add_im()
            self.__add_curve()
            self.__has_generated = True

    def save(self, file):
        self.__generate_image()
        self.__board.save(file)

    def show(self):
        self.__generate_image()
        self.__board.show()

    def get_bytes(self):
        self.__generate_image()
        img_bytes = io.BytesIO()
        self.__board.save(img_bytes, format='png')

        return img_bytes.getvalue()

    def get_text(self):
        self.__generate_image()
        return self.__text


if __name__ == '__main__':
    cp = Captcha()
    cp.set_type('en')
    cp.set_font_file('seccode/PepRally.ttf')
    cp.set_font_size(60)
    cp.set_font_color(0, 0, 255)
    cp.set_font_rand_a(random.randint(10, 14))
    cp.set_font_rand_b(random.randint(2, 3))
    cp.set_font_rotate(10, 20)
    cp.set_font_distance(35)

    cp.set_board_width(180)
    cp.set_board_height(75)
    cp.set_board_bgcolor(255, 255, 255)

    cp.set_line_num(5)
    cp.set_line_width(2)
    cp.set_line_color(0, 0, 255)
    cp.set_line_rand_a(random.randint(15, 20))
    cp.set_line_rand_b(random.randint(5, 8))

    cp.save('text.png')
